package com.csun.cmny.provider.service.impl;

import com.csun.cmny.base.dto.LoginAuthDto;
import com.csun.cmny.base.enums.ErrorCodeEnum;
import com.csun.cmny.core.support.BaseService;
import com.csun.cmny.provider.dao.DmcWatchAlarmClockMapper;
import com.csun.cmny.provider.device.watch.WatchMessageHandler;
import com.csun.cmny.provider.exceptions.DmcBizException;
import com.csun.cmny.provider.model.domain.DmcDevice;
import com.csun.cmny.provider.model.domain.DmcWatchAlarmClock;
import com.csun.cmny.provider.model.dto.watch.WatchAlarmClockDto;
import com.csun.cmny.provider.model.dto.watch.WatchAlarmClockPageQuery;
import com.csun.cmny.provider.model.vo.watch.DmcWatchAlarmClockVo;
import com.csun.cmny.provider.resolver.CmnyMessageSource;
import com.csun.cmny.provider.service.DmcDeviceService;
import com.csun.cmny.provider.service.DmcWatchAlarmClockService;
import com.csun.cmny.provider.validator.DmcWatchAlarmClockAddValidator;
import com.csun.cmny.util.PubUtils;
import com.csun.cmny.util.PublicUtil;
import com.github.pagehelper.PageHelper;
import com.github.pagehelper.PageInfo;
import com.google.common.base.Preconditions;
import com.google.common.collect.Lists;
import org.apache.commons.lang.StringUtils;
import org.springframework.beans.BeanUtils;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.annotation.Resource;
import javax.validation.constraints.NotNull;
import java.util.List;
import java.util.Map;

/**
 * @Auther: chisj chisj@foxmal.com
 * @Date: 2019-01-24 15:04
 * @Description: the dmc watch alarm clock service impl
 */
@Service
@Transactional(rollbackFor = Exception.class)
public class DmcWatchAlarmClockServiceImpl extends BaseService<DmcWatchAlarmClock>
        implements DmcWatchAlarmClockService {

    @Resource
    private CmnyMessageSource cmnyMessageSource;

    @Resource
    private DmcDeviceService dmcDeviceService;

    @Resource
    private DmcWatchAlarmClockMapper dmcWatchAlarmClockMapper;
    @Resource
    private WatchMessageHandler watchMessageHandler;

    /**
     * query dmc watch alarm clock list with page
     *
     * @param watchAlarmClockPageQuery  watch alarm clock page query
     *
     * @return the page info
     */
    @Override
    public PageInfo queryDmcWatchAlarmClockListWithPage(WatchAlarmClockPageQuery watchAlarmClockPageQuery) {

        PageHelper.startPage(watchAlarmClockPageQuery.getPageNum(), watchAlarmClockPageQuery.getPageSize());

        List<DmcWatchAlarmClockVo> dmcWatchAlarmClockVoList = dmcWatchAlarmClockMapper
                .queryDmcWatchAlarmClockListWithPage(watchAlarmClockPageQuery);

        return new PageInfo<>(dmcWatchAlarmClockVoList);
    }

    /**
     * add dmc watch alarm clock
     *
     * @param watchAlarmClockDto watch alarm clock dto
     * @return the int.
     */
    @Override
    public Integer addDmcWatchAlarmClock(WatchAlarmClockDto watchAlarmClockDto,
                                         LoginAuthDto loginAuthDto) {

        // 1.判断设备是否存在 && 设备是否为孝心手环
        DmcDevice dmcDevice = dmcDeviceService.checkDeviceIsWatchByDeviceId(watchAlarmClockDto.getDeviceId());
        if (PublicUtil.isEmpty(dmcDevice)) {
            throw new DmcBizException(ErrorCodeEnum.DMC100610014, watchAlarmClockDto.getDeviceId());
        }

        /**
         * 智能添加六条 -- 多了后手环不会提醒
         */
        DmcWatchAlarmClock dmcWatchAlarmClockQuery = new DmcWatchAlarmClock();
        dmcWatchAlarmClockQuery.setDeviceId(watchAlarmClockDto.getDeviceId());
        int count = dmcWatchAlarmClockMapper.selectCount(dmcWatchAlarmClockQuery);
        if (count >= 5) {
            throw new DmcBizException(ErrorCodeEnum.DMC100611001);
        }

        // 2.校验 闹钟类型 0-吃药提醒 1-喝水提醒 2-运动提醒 3-自定义1 4-自定义2 5-自定义3
        Preconditions.checkArgument(watchAlarmClockDto.getClockType() != null,
                cmnyMessageSource.getMessage("watch.alarm.clock.type.null", null));
        Preconditions.checkArgument(watchAlarmClockDto.getClockType() >= 0
                        && watchAlarmClockDto.getClockType() <= 5,
                cmnyMessageSource.getMessage("watch.alarm.clock.type.range", null));

        // 3.校验 闹钟开关 0-关 1-开
        Preconditions.checkArgument(watchAlarmClockDto.getClockSwitch() != null,
                cmnyMessageSource.getMessage("watch.alarm.clock.switch.null", null));
        Preconditions.checkArgument(watchAlarmClockDto.getClockSwitch() >= 0
                        && watchAlarmClockDto.getClockSwitch() <= 1,
                cmnyMessageSource.getMessage("watch.alarm.clock.switch.range", null));

        // 4.校验 闹钟时间 范围0000-2359
        Preconditions.checkArgument(watchAlarmClockDto.getClockTime() != null,
                cmnyMessageSource.getMessage("watch.alarm.clock.time.null", null));
        Preconditions.checkArgument(PubUtils.isPeriod(watchAlarmClockDto.getClockTime()),
                cmnyMessageSource.getMessage("watch.config.clock.time.pattern", null));

        // 5.校验 周一是否重复 0-不重复 1-重复
        Preconditions.checkArgument(watchAlarmClockDto.getRepeatMon() != null,
                cmnyMessageSource.getMessage("watch.alarm.clock.repeat.mon.null", null));
        Preconditions.checkArgument(watchAlarmClockDto.getRepeatMon() >= 0
                        && watchAlarmClockDto.getRepeatMon() <= 1,
                cmnyMessageSource.getMessage("watch.alarm.clock.repeat.mon.range", null));

        // 6.校验 周二是否重复 0-不重复 1-重复
        Preconditions.checkArgument(watchAlarmClockDto.getRepeatTue() != null,
                cmnyMessageSource.getMessage("watch.alarm.clock.repeat.tue.null", null));
        Preconditions.checkArgument(watchAlarmClockDto.getRepeatTue() >= 0
                        && watchAlarmClockDto.getRepeatTue() <= 1,
                cmnyMessageSource.getMessage("watch.alarm.clock.repeat.tue.range", null));

        // 7.校验 周三是否重复 0-不重复 1-重复
        Preconditions.checkArgument(watchAlarmClockDto.getRepeatWed() != null,
                cmnyMessageSource.getMessage("watch.alarm.clock.repeat.wed.null", null));
        Preconditions.checkArgument(watchAlarmClockDto.getRepeatWed() >= 0
                        && watchAlarmClockDto.getRepeatWed() <= 1,
                cmnyMessageSource.getMessage("watch.alarm.clock.repeat.wed.range", null));

        // 8.校验 周四是否重复 0-不重复 1-重复
        Preconditions.checkArgument(watchAlarmClockDto.getRepeatThu() != null,
                cmnyMessageSource.getMessage("watch.alarm.clock.repeat.thu.null", null));
        Preconditions.checkArgument(watchAlarmClockDto.getRepeatThu() >= 0
                        && watchAlarmClockDto.getRepeatThu() <= 1,
                cmnyMessageSource.getMessage("watch.alarm.clock.repeat.thu.range", null));

        // 9.校验 周五是否重复 0-不重复 1-重复
        Preconditions.checkArgument(watchAlarmClockDto.getRepeatFri() != null,
                cmnyMessageSource.getMessage("watch.alarm.clock.repeat.fri.null", null));
        Preconditions.checkArgument(watchAlarmClockDto.getRepeatFri() >= 0
                        && watchAlarmClockDto.getRepeatFri() <= 1,
                cmnyMessageSource.getMessage("watch.alarm.clock.repeat.fri.range", null));

        // 10.校验 周六是否重复 0-不重复 1-重复
        Preconditions.checkArgument(watchAlarmClockDto.getRepeatSat() != null,
                cmnyMessageSource.getMessage("watch.alarm.clock.repeat.sat.null", null));
        Preconditions.checkArgument(watchAlarmClockDto.getRepeatSat() >= 0
                        && watchAlarmClockDto.getRepeatSat() <= 1,
                cmnyMessageSource.getMessage("watch.alarm.clock.repeat.sat.range", null));

        // 11.校验 周日是否重复 0-不重复 1-重复
        Preconditions.checkArgument(watchAlarmClockDto.getRepeatSun() != null,
                cmnyMessageSource.getMessage("watch.alarm.clock.repeat.sun.null", null));
        Preconditions.checkArgument(watchAlarmClockDto.getRepeatSun() >= 0
                        && watchAlarmClockDto.getRepeatSun() <= 1,
                cmnyMessageSource.getMessage("watch.alarm.clock.repeat.sun.range", null));

        boolean flag = false;
        // 当闹钟类型大于2时校验
        if (watchAlarmClockDto.getClockType() > 2) {
            flag = true;
            // 12.校验 铃声类型 0 amr
            Preconditions.checkArgument(watchAlarmClockDto.getVoiceType() != null,
                    cmnyMessageSource.getMessage("watch.alarm.clock.voice.type.null", null));
            Preconditions.checkArgument(watchAlarmClockDto.getVoiceType().equals(0),
                    cmnyMessageSource.getMessage("watch.alarm.clock.voice.type.range", null));

            // 13.校验 铃声文件
            Preconditions.checkArgument(watchAlarmClockDto.getVoiceData() != null,
                    cmnyMessageSource.getMessage("watch.alarm.clock.voice.data.null", null));
        }

        // 14.添加闹钟
        DmcWatchAlarmClock dmcWatchAlarmClock = new DmcWatchAlarmClock();
        BeanUtils.copyProperties(watchAlarmClockDto, dmcWatchAlarmClock);
        dmcWatchAlarmClock.setUpdateInfo(loginAuthDto);
        Integer result = dmcWatchAlarmClockMapper.insertSelective(dmcWatchAlarmClock);

        String clocks = buildClocksByDeviceId(watchAlarmClockDto.getDeviceId());

        // 16.下发闹钟
        if (result > 0) {
            watchMessageHandler.setAlarmClock(dmcDevice.getDeviceNo(), clocks);
            if (flag) {
                watchMessageHandler.setVoiceFile(dmcDevice.getDeviceNo());
            }
        }

        return result;
    }

    /**
     * 构建闹钟字符串
     *
     * @param deviceId  设备Id
     *
     * @return  闹钟类型[0-吃药提醒 1-喝水提醒 2-运动提醒 3-自定义 1]&
     *          闹钟开关[0-为关, 1-为开]&
     *          闹钟时间&重复规律[1111100，表示周一到周五工作，周六/周日不工作]
     *          多组闹钟用 % 分隔
     */
    private String buildClocksByDeviceId(Long deviceId) {

        DmcWatchAlarmClock dmcWatchAlarmClock = new DmcWatchAlarmClock();
        dmcWatchAlarmClock.setDeviceId(deviceId);

        List<DmcWatchAlarmClock> list = select(dmcWatchAlarmClock);
        if (PublicUtil.isEmpty(list)) {
            return "";
        }

        List<String> clocks = Lists.newArrayList();
        for (DmcWatchAlarmClock c : list) {
            StringBuilder sb = new StringBuilder();
            sb.append(c.getClockType());
            sb.append("&");
            sb.append(c.getClockSwitch());
            sb.append("&");
            sb.append(c.getClockTime());
            sb.append("&");
            sb.append(c.getRepeatMon());
            sb.append(c.getRepeatTue());
            sb.append(c.getRepeatWed());
            sb.append(c.getRepeatThu());
            sb.append(c.getRepeatFri());
            sb.append(c.getRepeatSat());
            sb.append(c.getRepeatSun());
            clocks.add(sb.toString());
        }

        return StringUtils.join(clocks, "%");
    }

    /**
     * delete dmc watch alarm clock by id
     *
     * @param id
     *
     * @return the int.
     */
    @Override
    public Integer deleteDmcWatchAlarmClockById(Long id) {

        // 1.校验 闹钟Id
        Preconditions.checkArgument(id != null,
                cmnyMessageSource.getMessage("watch.alarm.clock.id.null", null));
        DmcWatchAlarmClock dmcWatchAlarmClockQuery = dmcWatchAlarmClockMapper.selectByPrimaryKey(id);
        if (PublicUtil.isEmpty(dmcWatchAlarmClockQuery)) {
            throw new DmcBizException(ErrorCodeEnum.DMC100610019, id);
        }

        // 2.删除闹钟
        Integer result = dmcWatchAlarmClockMapper.deleteByPrimaryKey(id);

        String clocks = buildClocksByDeviceId(dmcWatchAlarmClockQuery.getDeviceId());

        // 3.下发闹钟
        if (result > 0) {
            DmcDevice dmcDevice = dmcDeviceService.selectByKey(dmcWatchAlarmClockQuery.getDeviceId());
            watchMessageHandler.setAlarmClock(dmcDevice.getDeviceNo(), clocks);
        }


        return result;
    }

    /**
     * edit dmc watch alarm clock
     *
     * @param watchAlarmClockDto watch alarm clock dto
     * @param loginAuthDto       login auth dto
     * @return the int.
     */
    @Override
    public Integer editDmcWatchAlarmClock(WatchAlarmClockDto watchAlarmClockDto, LoginAuthDto loginAuthDto) {

        // 1.校验 闹钟Id
        Preconditions.checkArgument(watchAlarmClockDto.getId() != null,
                cmnyMessageSource.getMessage("watch.alarm.clock.id.null", null));
        DmcWatchAlarmClock dmcWatchAlarmClockQuery = dmcWatchAlarmClockMapper.selectByPrimaryKey(watchAlarmClockDto.getId());
        if (PublicUtil.isEmpty(dmcWatchAlarmClockQuery)) {
            throw new DmcBizException(ErrorCodeEnum.DMC100610019, watchAlarmClockDto.getDeviceId());
        }

        // 2.判断设备是否存在 && 设备是否为孝心手环
        if (PublicUtil.isNotEmpty(watchAlarmClockDto.getDeviceId())) {
            DmcDevice dmcDevice = dmcDeviceService.checkDeviceIsWatchByDeviceId(watchAlarmClockDto.getDeviceId());
            if (PublicUtil.isEmpty(dmcDevice)) {
                throw new DmcBizException(ErrorCodeEnum.DMC100610014, watchAlarmClockDto.getDeviceId());
            }
        }

        // 3.校验 闹钟类型 0-吃药提醒 1-喝水提醒 2-运动提醒 3-自定义1 4-自定义2 5-自定义3
        if (PublicUtil.isNotEmpty(watchAlarmClockDto.getClockType())) {
            Preconditions.checkArgument(watchAlarmClockDto.getClockType() != null,
                    cmnyMessageSource.getMessage("watch.alarm.clock.type.null", null));
            Preconditions.checkArgument(watchAlarmClockDto.getClockType() >= 0
                            && watchAlarmClockDto.getClockType() <= 5,
                    cmnyMessageSource.getMessage("watch.alarm.clock.type.range", null));
        }


        // 4.校验 闹钟开关 0-关 1-开
        if (PublicUtil.isNotEmpty(watchAlarmClockDto.getClockSwitch())) {
            Preconditions.checkArgument(watchAlarmClockDto.getClockSwitch() != null,
                    cmnyMessageSource.getMessage("watch.alarm.clock.switch.null", null));
            Preconditions.checkArgument(watchAlarmClockDto.getClockSwitch() >= 0
                            && watchAlarmClockDto.getClockSwitch() <= 1,
                    cmnyMessageSource.getMessage("watch.alarm.clock.switch.range", null));
        }

        // 5.校验 闹钟时间 范围0000-2359
        if (PublicUtil.isNotEmpty(watchAlarmClockDto.getClockSwitch())) {
            Preconditions.checkArgument(watchAlarmClockDto.getClockTime() != null,
                    cmnyMessageSource.getMessage("watch.alarm.clock.time.null", null));
            Preconditions.checkArgument(PubUtils.isPeriod(watchAlarmClockDto.getClockTime()),
                    cmnyMessageSource.getMessage("watch.config.clock.time.pattern", null));
        }

        // 6.校验 周一是否重复 0-不重复 1-重复
        if (PublicUtil.isNotEmpty(watchAlarmClockDto.getRepeatMon())) {
            Preconditions.checkArgument(watchAlarmClockDto.getRepeatMon() != null,
                    cmnyMessageSource.getMessage("watch.alarm.clock.repeat.mon.null", null));
            Preconditions.checkArgument(watchAlarmClockDto.getRepeatMon() >= 0
                            && watchAlarmClockDto.getRepeatMon() <= 1,
                    cmnyMessageSource.getMessage("watch.alarm.clock.repeat.mon.range", null));
        }

        // 7.校验 周二是否重复 0-不重复 1-重复
        if (PublicUtil.isNotEmpty(watchAlarmClockDto.getRepeatTue())) {
            Preconditions.checkArgument(watchAlarmClockDto.getRepeatTue() != null,
                    cmnyMessageSource.getMessage("watch.alarm.clock.repeat.tue.null", null));
            Preconditions.checkArgument(watchAlarmClockDto.getRepeatTue() >= 0
                            && watchAlarmClockDto.getRepeatTue() <= 1,
                    cmnyMessageSource.getMessage("watch.alarm.clock.repeat.tue.range", null));
        }

        // 8.校验 周三是否重复 0-不重复 1-重复
        if (PublicUtil.isNotEmpty(watchAlarmClockDto.getRepeatWed())) {
            Preconditions.checkArgument(watchAlarmClockDto.getRepeatWed() != null,
                    cmnyMessageSource.getMessage("watch.alarm.clock.repeat.wed.null", null));
            Preconditions.checkArgument(watchAlarmClockDto.getRepeatWed() >= 0
                            && watchAlarmClockDto.getRepeatWed() <= 1,
                    cmnyMessageSource.getMessage("watch.alarm.clock.repeat.wed.range", null));
        }

        // 9.校验 周四是否重复 0-不重复 1-重复
        if (PublicUtil.isNotEmpty(watchAlarmClockDto.getRepeatThu())) {
            Preconditions.checkArgument(watchAlarmClockDto.getRepeatThu() != null,
                    cmnyMessageSource.getMessage("watch.alarm.clock.repeat.thu.null", null));
            Preconditions.checkArgument(watchAlarmClockDto.getRepeatThu() >= 0
                            && watchAlarmClockDto.getRepeatThu() <= 1,
                    cmnyMessageSource.getMessage("watch.alarm.clock.repeat.thu.range", null));
        }

        // 10.校验 周五是否重复 0-不重复 1-重复
        if (PublicUtil.isNotEmpty(watchAlarmClockDto.getRepeatFri())) {
            Preconditions.checkArgument(watchAlarmClockDto.getRepeatFri() != null,
                    cmnyMessageSource.getMessage("watch.alarm.clock.repeat.fri.null", null));
            Preconditions.checkArgument(watchAlarmClockDto.getRepeatFri() >= 0
                            && watchAlarmClockDto.getRepeatFri() <= 1,
                    cmnyMessageSource.getMessage("watch.alarm.clock.repeat.fri.range", null));
        }

        // 11.校验 周六是否重复 0-不重复 1-重复
        if (PublicUtil.isNotEmpty(watchAlarmClockDto.getRepeatSat())) {
            Preconditions.checkArgument(watchAlarmClockDto.getRepeatSat() != null,
                    cmnyMessageSource.getMessage("watch.alarm.clock.repeat.sat.null", null));
            Preconditions.checkArgument(watchAlarmClockDto.getRepeatSat() >= 0
                            && watchAlarmClockDto.getRepeatSat() <= 1,
                    cmnyMessageSource.getMessage("watch.alarm.clock.repeat.sat.range", null));
        }

        // 12.校验 周日是否重复 0-不重复 1-重复
        if (PublicUtil.isNotEmpty(watchAlarmClockDto.getRepeatSun())) {
            Preconditions.checkArgument(watchAlarmClockDto.getRepeatSun() != null,
                    cmnyMessageSource.getMessage("watch.alarm.clock.repeat.sun.null", null));
            Preconditions.checkArgument(watchAlarmClockDto.getRepeatSun() >= 0
                            && watchAlarmClockDto.getRepeatSun() <= 1,
                    cmnyMessageSource.getMessage("watch.alarm.clock.repeat.sun.range", null));
        }

        // 15.修改闹钟
        DmcWatchAlarmClock dmcWatchAlarmClock = new DmcWatchAlarmClock();

        boolean flag = false;
        // 当闹钟类型大于2时校验
        if (watchAlarmClockDto.getClockType() > 2) {
            flag = true;
            // 13.校验 铃声类型 0 amr
            if (PublicUtil.isNotEmpty(watchAlarmClockDto.getVoiceType())) {
                Preconditions.checkArgument(watchAlarmClockDto.getVoiceType() != null,
                        cmnyMessageSource.getMessage("watch.alarm.clock.voice.type.null", null));
                Preconditions.checkArgument(watchAlarmClockDto.getVoiceType().equals(0),
                        cmnyMessageSource.getMessage("watch.alarm.clock.voice.type.range", null));
            }

            // 14.校验 铃声文件
            if (PublicUtil.isNotEmpty(watchAlarmClockDto.getVoiceData())) {
                Preconditions.checkArgument(watchAlarmClockDto.getVoiceData() != null,
                        cmnyMessageSource.getMessage("watch.alarm.clock.voice.data.null", null));
            }

            dmcWatchAlarmClock.setIsSend(0);
        }

        // 不能修改设备Id
        dmcWatchAlarmClock.setDeviceId(null);
        BeanUtils.copyProperties(watchAlarmClockDto, dmcWatchAlarmClock);
        dmcWatchAlarmClock.setUpdateInfo(loginAuthDto);
        Integer result = dmcWatchAlarmClockMapper.updateByPrimaryKeySelective(dmcWatchAlarmClock);

        // 16.下发闹钟
        if (result > 0) {
            String clocks = buildClocksByDeviceId(dmcWatchAlarmClockQuery.getDeviceId());
            DmcDevice dmcDevice = dmcDeviceService.selectByKey(dmcWatchAlarmClockQuery.getDeviceId());
            watchMessageHandler.setAlarmClock(dmcDevice.getDeviceNo(), clocks);
            if (flag) {
                watchMessageHandler.setVoiceFile(dmcDevice.getDeviceNo());
            }
        }

        return result;
    }

    /**
     * remove dmc watch alarm clock
     *
     * @param deviceId device id
     * @return the int.
     */
    @Override
    public Integer removeDmcWatchAlarmClock(Long deviceId) {

        DmcWatchAlarmClock dmcWatchAlarmClock = new DmcWatchAlarmClock();
        dmcWatchAlarmClock.setDeviceId(deviceId);

        return dmcWatchAlarmClockMapper.delete(dmcWatchAlarmClock);
    }

    /**
     * get one wait send alarm clock by device id
     *
     * @param deviceId  device id
     *
     * @return  the dmc watch alarm clock
     */
    public DmcWatchAlarmClock getOneWaitSendAlarmClockByDeviceId(Long deviceId) {

        return dmcWatchAlarmClockMapper.getOneWaitSendAlarmClockByDeviceId(deviceId);
    }

    /**
     * send alarm clock finish
     *
     * @param id
     *
     * @return  the int.
     */
    public Integer sendAlarmClockFinish(Long id) {

        DmcWatchAlarmClock dmcWatchAlarmClock = new DmcWatchAlarmClock();
        dmcWatchAlarmClock.setIsSend(1);
        dmcWatchAlarmClock.setId(id);

        return dmcWatchAlarmClockMapper.updateByPrimaryKeySelective(dmcWatchAlarmClock);
    }

    /**
     * select watch alarm clock by device id
     *
     * @param deviceId the device id
     *
     * @return the map
     */
    @Override
    public Map<String, Object> selectByDeviceId(Long deviceId) {

        return dmcWatchAlarmClockMapper.selectByDeviceId(deviceId);
    }
}
